﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using LLoger;

using LimitatorInterfaces;

namespace AlertSystem
{
    internal class ServiceLoader : ServiceBase
    {
        private KeyValuePair<string, string>[] _paramList;

        public ServiceLoader(string configPath, ILogWriter logWriter)
            : base(configPath, logWriter)
        {
        }

        public IEnumerable<KeyValuePair<ILoadProvider<object>, ILoadParser<IAlertItem>>> LoadServices()
        {
            if (!CheckConfig())
            {
                var msg = "Загрузка сервисов невозможна: отсутсвуют определения в конфигурации";
                LogWriter.WriteLine(msg);
                throw new ServiceLoaderException(msg);
            }

            var services = new List<KeyValuePair<ILoadProvider<object>, ILoadParser<IAlertItem>>>();

            foreach (var paramItem in _paramList)
            {
                if (paramItem.Key == Res.ServiceLoaderKey)
                    continue;

                var param = paramItem.Value.Split(';');

                if (param.Length != 3)
                {
                    LogWriter.WriteLine("Параметр {0} имеет неверный формат значения: {1}", paramItem.Key, paramItem.Value);
                    continue;
                }

                string module = param[0];

                if (!File.Exists(module))
                {
                    LogWriter.WriteLine("Модуль {0} не найден. Парамет загрузки: {1} ({2})", module, paramItem.Key, paramItem.Value);
                    continue;
                }

                try
                {
                    ILoadProvider<object> loadProvider = InstantiateType<ILoadProvider<object>>(module, param[1]);
                    ILoadParser<IAlertItem> loadParser = InstantiateType<ILoadParser<IAlertItem>>(module, param[2]);

                    services.Add(new KeyValuePair<ILoadProvider<object>, ILoadParser<IAlertItem>>(loadProvider,
                                                                                                  loadParser));
                }
                catch (Exception ex)
                {
                    LogWriter.WriteLine("Не удалось загрузить и создать сервисы. Параметр конфигурации:{0} ({1})", paramItem.Key, paramItem.Value);
                }

            }

            return services;
        }

        private T InstantiateType<T>(string module, string typeName)
        {
            string msg = "";
            Exception errObj = null;

            try
            {
                Assembly asm = Assembly.Load(AssemblyName.GetAssemblyName(module));

                var type = asm.GetExportedTypes().SingleOrDefault(t => t.Name.StartsWith(typeName));

                if (type == null)
                    throw new ServiceLoaderException("Не обнаружен класс сервиса");

                return (T) Activator.CreateInstance(type);
            }
            catch (FileLoadException ex)
            {
                msg = "Ошибка при загрузке сервисной библиотеки " + module;
                errObj = new ServiceLoaderException(msg, ex);
            }
            catch (BadImageFormatException ex)
            {
                msg = "Ошибка при загрузке сервисной библиотеки " + module;
                errObj = new ServiceLoaderException(msg, ex);
            }
            catch (InvalidCastException ex)
            {
                msg = string.Format("Указан неверный тип сервисного класса: класс {0} должен реализовывать интерфейс {1}",typeName, typeof(T).Name);
            }
            catch (ServiceLoaderException ex)
            {
                errObj = ex;
            }
            catch (Exception ex)
            {
                msg = "Неопознанное исключение при загрузке или инициализации сервиса";
                errObj = new ServiceLoaderException(msg, ex);
            }

            //LogWriter.WriteLine(errObj, msg);
            throw errObj;

        }

        private bool CheckConfig()
        {
            _paramList = GetStartWithSettingValues(GetSettingsValue(Res.ServiceLoaderKey));

            return _paramList != null && _paramList.Length > 0;
        }
    }
}
